/*!
\copyright  Copyright (c) 2022-2024 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\file
\ingroup    swift_pair
\brief
*/

#include "swift_pair_advertising.h"
#include "swift_pair.h"
#include "swift_pair_config.h"
#include "le_advertising_manager.h"
#include "bt_device.h"
#include "class_of_device.h"

#include <bdaddr.h>
#include <local_name.h>
#include <logging.h>

/* Mandatory GAP Flags. Will be added automatically by LEAM, so simply account for that space */
#define SWIFT_PAIR_GAP_FLAGS_LENGTH     3

#define SWIFT_PAIR_MICROSOFT_VENDOR_ID  0x0006
#define SWIFT_PAIR_MICROSOFT_BEACON_ID  0x03
#define SWIFT_PAIR_RESERVED_RSSI_BYTE   0x80

#define SUB_SCENARIO_OFFSET             5
#define RESERVED_RSSI_OFFSET            6

static uint8 sp_payload[MAX_ADVERT_SIZE] =
{
    0, // length,
    ble_ad_type_manufacturer_specific_data,
    SWIFT_PAIR_MICROSOFT_VENDOR_ID & 0xFF,
    (SWIFT_PAIR_MICROSOFT_VENDOR_ID >> 8) & 0xFF,
    SWIFT_PAIR_MICROSOFT_BEACON_ID,
    0, // sub scenario
    SWIFT_PAIR_RESERVED_RSSI_BYTE,
};

static le_adv_item_data_t sp_advert_payload =
{
    .size = 0,
    .data = sp_payload
};

static le_adv_item_handle swift_pair_registered_handle = NULL;
static bool is_discoverable = FALSE;
static bool is_advertising_paused = FALSE;

static inline bool swiftPair_ModeIncludesIdentityAddress(swift_pair_pairing_mode_t mode)
{
    return (mode == swift_pair_pairing_mode_bredr_only);
}

static inline bool swiftPair_ModeIncludesClassOfDevice(swift_pair_pairing_mode_t mode)
{
    return (mode == swift_pair_pairing_mode_bredr_only || mode == swift_pair_pairing_mode_dual_mode);
}

static inline uint8 swiftPair_GetSubScenarioForMode(swift_pair_pairing_mode_t mode)
{
    uint8 sub_scenario = 0x02;
    switch(mode)
    {
        case swift_pair_pairing_mode_le_only:
            sub_scenario = 0x00;
            break;
        case swift_pair_pairing_mode_bredr_only:
            sub_scenario = 0x01;
            break;
        case swift_pair_pairing_mode_dual_mode:
            sub_scenario = 0x02;
            break;
        default:
            DEBUG_LOG_ERROR("swiftPair_GetSubScenarioForMode: invalid mode");
            break;
    }
    return sub_scenario;
}

static inline uint8 swiftPair_GetMandatoryDataSizeForMode(swift_pair_pairing_mode_t mode)
{
    uint8 data_size = 10;
    switch(mode)
    {
        case swift_pair_pairing_mode_le_only:
            data_size = 7;
            break;
        case swift_pair_pairing_mode_bredr_only:
            data_size = 16;
            break;
        case swift_pair_pairing_mode_dual_mode:
            data_size = 10;
            break;
        default:
            DEBUG_LOG_ERROR("swiftPair_GetMandatoryDataSizeForMode: invalid mode");
            break;
    }
    return data_size;
}

static inline uint8 swiftPair_GetMaxNameLength(swift_pair_pairing_mode_t mode, swift_pair_advertisment_type_t advert_type)
{
    uint8 advert_limit = ((advert_type == swift_pair_use_legacy_advertising) ? 31 : 255);
    return (MIN(MAX_ADVERT_SIZE, advert_limit) - (AD_DATA_HEADER_SIZE + swiftPair_GetMandatoryDataSizeForMode(mode) + SWIFT_PAIR_GAP_FLAGS_LENGTH));
}

static const uint8* swiftPair_GetLocalName(uint16 *name_length, bool *truncated)
{
    const uint8* name = LocalName_GetPrefixedName(name_length);
    uint8 max_name_length = swiftPair_GetMaxNameLength(SwiftPair_GetConfig()->mode, SwiftPair_GetConfig()->advertisment_type);
    *truncated = FALSE;
    if (*name_length > max_name_length)
    {
        DEBUG_LOG_WARN("swiftPair_GetLocalName truncating name");
        *name_length = max_name_length;
        *truncated = TRUE;
    }
    return name;
}

static inline bool swiftPair_ShouldIncludeDeviceNameInAdvertisment(swift_pair_device_name_option_t name_option)
{
    return (name_option == swift_pair_include_device_name_as_swift_pair_data
            || name_option == swift_pair_include_device_name_as_seperate_ad_type);
}

static bool swiftPair_PopulateAdvertPayload(void)
{
    swift_pair_config_t * config = SwiftPair_GetConfig();

    sp_payload[AD_DATA_LENGTH_OFFSET] = (swiftPair_GetMandatoryDataSizeForMode(config->mode) - 1);
    sp_payload[SUB_SCENARIO_OFFSET] = swiftPair_GetSubScenarioForMode(config->mode);
    sp_payload[RESERVED_RSSI_OFFSET] = SWIFT_PAIR_RESERVED_RSSI_BYTE;

    unsigned index = RESERVED_RSSI_OFFSET+1;

    if(swiftPair_ModeIncludesIdentityAddress(config->mode))
    {
        bdaddr self_address;
        BdaddrSetZero(&self_address);
        if(appDeviceGetPrimaryBdAddr(&self_address) 
            || appDeviceGetMyBdAddr(&self_address))
        {
            sp_payload[index++]  = self_address.lap;
            sp_payload[index++]  = (self_address.lap >> 8);
            sp_payload[index++]  = (self_address.lap >> 16);
            sp_payload[index++]  = self_address.uap;
            sp_payload[index++]  = self_address.nap;
            sp_payload[index++] = (self_address.nap >> 8);
        }
        else
        {
            DEBUG_LOG_ERROR("swiftPair_GetData: no self address to advertise");
            sp_advert_payload.size = 0;
            return FALSE;
        }
    }
    if(swiftPair_ModeIncludesClassOfDevice(config->mode))
    {
        uint32 class_of_device = ClassOfDevice_GetCoD();
        sp_payload[index++]  = (class_of_device & 0xFF);
        sp_payload[index++]  = (class_of_device >> 8) & 0xFF;
        sp_payload[index++]  = (class_of_device >> 16) & 0xFF;
    }

    if(swiftPair_ShouldIncludeDeviceNameInAdvertisment(config->device_name_option))
    {
        uint16 name_len = 0;
        bool name_truncated = FALSE;
        const uint8* name = swiftPair_GetLocalName(&name_len, &name_truncated);
        if(name_len && name)
        {
            if(config->device_name_option == swift_pair_include_device_name_as_swift_pair_data)
            {
                sp_payload[AD_DATA_LENGTH_OFFSET] += name_len;
            }
            else
            {
                sp_payload[index++] = name_len + 1;
                sp_payload[index++] = name_truncated ? ble_ad_type_shortened_local_name : ble_ad_type_complete_local_name;
            }
            memcpy(&sp_payload[index], name, name_len);
            index += name_len;
        }
        else
        {
            DEBUG_LOG_ERROR("swiftPair_GetData: no device name to advertise");
        }
    }
    memset(&sp_payload[index], 0, (sizeof(sp_payload) - index));
    sp_advert_payload.size = index;
    sp_advert_payload.data = sp_payload;
    return TRUE;
}

static inline bool swiftPair_CanAdvertise(void)
{
    return (is_discoverable && !is_advertising_paused);
}

static bool swiftPair_GetItemData(le_adv_item_data_t * item)
{
    PanicNull(item);
    bool can_advertise = FALSE;
    if(swiftPair_CanAdvertise())
    {
        *item = sp_advert_payload;
        can_advertise = TRUE;
    }
    else
    {
        memset(item, 0, sizeof(*item));
    }
    return can_advertise;
}

static unsigned swiftPair_GetItemDataSize(void)
{
    return (swiftPair_CanAdvertise() ? sp_advert_payload.size : 0);
}

static inline le_adv_data_type_t swiftPair_GetAdvertType(void)
{
    le_adv_data_type_t adv_type = le_adv_type_extended_connectable;
    if(SwiftPair_GetConfig()->mode == swift_pair_pairing_mode_bredr_only)
    {
        adv_type = ((SwiftPair_GetConfig()->advertisment_type == swift_pair_use_legacy_advertising) ?
                        le_adv_type_legacy_non_connectable_non_scannable :
                        le_adv_type_extended_non_scannable_non_connectable);
    }
    else
    {
        adv_type = ((SwiftPair_GetConfig()->advertisment_type == swift_pair_use_legacy_advertising) ?
                        le_adv_type_legacy_connectable_scannable :
                        le_adv_type_extended_connectable);
    }
    return adv_type;
}

static bool swiftPair_GetItemInfo(le_adv_item_info_t * info)
{
    PanicNull(info);
    *info = (le_adv_item_info_t){ .placement = le_adv_item_data_placement_advert,
                                        .type = swiftPair_GetAdvertType(),
                                        .data_size = swiftPair_GetItemDataSize(),
                                };
    return TRUE;
}

le_adv_item_callback_t swiftPair_AdvertisingManagerCallback =
{
    .GetItemData = &swiftPair_GetItemData,
    .GetItemInfo = &swiftPair_GetItemInfo,
};

bool SwiftPair_SetupLeAdvertisingData(void)
{
    swiftPair_PopulateAdvertPayload();
    if(!swift_pair_registered_handle)
    {
        swift_pair_registered_handle = LeAdvertisingManager_RegisterAdvertisingItemCallback(NULL, &swiftPair_AdvertisingManagerCallback);
    }
    else
    {
        LeAdvertisingManager_UpdateAdvertisingItem(swift_pair_registered_handle);
    }
    
    return (swift_pair_registered_handle ? TRUE : FALSE);
}

static bool swiftPair_UpdateLeAdvertisingData(void)
{
    swiftPair_PopulateAdvertPayload();
    return (swift_pair_registered_handle ? LeAdvertisingManager_UpdateAdvertisingItem(swift_pair_registered_handle) : FALSE);
}

void SwiftPair_PauseAdvertising(bool pause)
{
    is_advertising_paused = pause;
    swiftPair_UpdateLeAdvertisingData();
}

void SwiftPair_SetDiscoverable(bool discoverable)
{
    is_discoverable = discoverable;
    swiftPair_UpdateLeAdvertisingData();
}

void SwiftPair_DisableAdvertising(void)
{
    if(swift_pair_registered_handle)
    {
        LeAdvertisingManager_UnregisterAdvertisingItem(swift_pair_registered_handle);
        swift_pair_registered_handle = NULL;
    }
}
